#!/usr/bin/python2.5
# -*- coding: utf-8 -*-

from __future__ import with_statement
import os
import time
import subprocess
import signal
import sys
import init_import

from filelocker import FileLocker

class register(object):
    """
        register class , to registe process 

    """
    def __init__(self, **kwargs):
        self._process_info_ = dict(kwargs)
        self._process_num_  = 0 

    def add_process(self, *process_name_and_path ):
        if process_name_and_path and len(process_name_and_path) == 2:
            process_name, process_path = process_name_and_path
            self._process_info_[process_name] = process_path
        else:
            return

    def del_process(self, processname ):
        if not processname:
            return 
        elif self._process_info_.has_key(processname):
            self._process_info_.pop(processname)

    def clear_processes(self):
        self._process_info_.clear()

    def get_process_names(self):
        return self._process_info_.keys()

    def get_process_path(self, process_name):
        if processname and self._process_info_.has_key(processname):
            return self._process_info_[processname]
        return ""

    def modify_process_path(self, *process_name_and_path ):
        return self.add_process(process_name_and_path)


class monitor(object):
    """
        monitor of process 
    """
    def __init__(self, **process_info):
        self._register = register(process_info)
        self._stoped_process_pids = list()

    def run(self, check_sec = 20 ):
        """
            write self pid to file 
        """
        with open("process_daemon.pid","wb") as fp:
            fp.write(str(os.getpid()))

        try :
            map(self._start_process, self._register.iteritems())
        except Exception, e :
            print "map error at %s, error:%s" %(__file__, e)

        while 1:
            self._stoped_process_pids.clear()
            for k in self._register.keys():
                if not self._is_running(k):
                    self._stoped_process_pids.append(k)
            if self._stoped_process_pids:
                try:
                    map(self._start_process, self._stoped_process_pids)
                except Exception, e:
                    print "map error at %s, error:%s" %(__file__, e)
                    pass
            sleep(check_sec)

    def quit(self):
        sched_pid = None
        try:
            with open("process_daemon.pid","rb") as fp:
                sched_pid = fp.read().strip()
            if sched_pid:
                sched_pid = int(sched_pid)
                try:
                    os.kill(sched_pid,signal.SIGINT)
                    os.remove("process_daemon.pid")
                except Exception, e:
                    print "error at %s, error:%s" %(__file__, e)
            map(self._quit_process, self._register.iteritems()) 
        except Exception, e:
            print "error at %s, error:%s" %(__file__, e)

    def stop(self):
        sched_pid = None
        try:
            with open("process_daemon.pid","rb") as fp:
                sched_pid = fp.read().strip()
            if sched_pid:
                sched_pid = int(sched_pid)
                try:
                    os.kill(sched_pid,signal.SIGINT)
                    os.remove("process_daemon.pid")
                except Exception, e:
                    print "error at %s, error:%s" %(__file__, e)
            map(self._term_process, self._register.iteritems()) 
        except Exception, e:
            print "map error at %s, error:%s" %(__file__, e)
                
    def restart(self):
        try:
            map(self._term_process, self._register.iteritems())
            map(self._start_process, self._register.iteritems())
        except Exception:
            print "map error at %s, error:%s" %(__file__, e)
          
    def update(self):
        self.restart()        

    def _start_process(self, process_path):
        if not process_path :
            return
        try:    
            p = subprocess.Popen(args = process_path, stdout=subprocess.PIPE, close_fds = True)
        except Exception, e:
            print "error at %s, error:%s" %(__file__, e)
        return 

    def _term_process(self, process_name):
        if process_name is None:
            return None
        pids = self._is_running( process_name )
        if pids:
            for pid in pids:
                try:
                    os.kill(int(pid), signal.SIGTERM)      
                except OSError:
                    return None
            return 1

        else:
            return None

    def _quit_process( processname ):
        """
            this function not support release process lock and 
            other resources, not suggested
        """
        if process_name is None:
            return None
        pids = self._is_running( process_name )
        if pids:
            for pid in pids:
                try:
                    os.kill(int(pid), signal.SIGQUIT)      
                except OSError:
                    return None
            return 1
        else:
            return None       

    def _is_running(self, process_name ):
        if process_name is None:
            return None
        pids = []
        try:    
            p = subprocess.Popen(["ps","-C",process_name,"-o","pid"],stdout=subprocess.PIPE, close_fds =  True)
            stdout = p.communicate()
        except Exception, e:
            print "error at %s, error:%s" %(__file__, e)
            return None
        if stdout and type(stdout) is tuple:
            pids = str(stdout).split("\\n")[1:-1]
            map(int,pids)
        
        return pids


def usage():
    print "Need for commands :\n \
    All commands be used to operate [YOUR PROCESSES] \n\
           <--start>    [START single process                 YOUR PROCESSES ]  \n\n\
           <--stop>     [STOP smoothly                        YOUR PROCESSES ]  \n\n\
           <--quit>     [QUIT roughly                         YOUR PROCESSES ]  \n\n\
           <--update>   [UPDATE smoothly                      YOUR PROCESSES   \n\
                         should ensure daemon of YOUR PROCESS is running     ]  \n\n"       

if __name__ == "__main__":

    if len(sys.argv) != 2:
        usage()
        exit(1)
    local_argv = None    
    if sys.argv[1].startswith("--"):
        local_argv = sys.argv[1][2:]

    try:
        process_mutex = FileLocker("PROCESSES_DAEMON%s" % local_argv)
        process_mutex.lock(True)
    except HasBeenLockedError:
        sys.exit(1)

    REGISTED_PROCESS = { "test_process_1": "/data/apps/sbin/test_process_1" ,
                         "test_process_2": "/data/apps/sbin/test_process_2" }

    try:
        Moniter = monitor(REGISTED_PROCESS)

        if   local_argv == "start":
            Moniter.run()

        elif local_argv == "stop":
            Moniter.stop()
            exit(0)

        elif local_argv == "update":
            Moniter.update()
            exit(0)

        elif local_argv == "quit":
            sched_main_quit()
            exit(0)
    finally:
        process_mutex.unlock()

